<?php
Yii::import('Core.extensions.web.helpers.FDom');
class PageWidgetService extends FServiceBase
{    
    /**
    * Get a PageWidget model given its ID
    * 
    * @param int id PageWidget ID
    * @return FServiceModel
    */
    public function get($params){
        $model = PageWidget::model()->findByPk($this->getParam($params, 'id',0));
        if (! $model)
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid ID.'));
        $this->result->processed('model', $model);
        return $this->result;
    }
    
    public function save($params) {
        /**
        * @var CModel
        */
        $model = $this->getModel($params['PageWidget'],'PageWidget');
        $isCopyData = $this->getParam($params, 'copy_data', false);
        $instanceName = $this->getParam($params, 'instacne_name', '');
        $this->result->processed('model', $model);
        
        if (! $model->validate())
        {
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget', 'Submitted data is missing or invalid.'));
            return $this->result;
        }
        elseif ($this->getParam($params, 'validateOnly',0) == TRUE)
            return $this->result;
        
        if ($isCopyData)
        {
            $result = FSM::_run('Cms.WidgetData.copy', array(
                'id'=>$model->widget_data_id,
                'name'=>$instanceName,
            ));
            if (!$result->hasErrors())
                $model->widget_data_id = (int) $result->model->id;
        }
        if (! $model->save(false))
            $this->result->fail(ERROR_HANDLING_DB, Yii::t('Cms.PageWidget','Error while saving submitted data into database.'));
        
        return $this->result;
    }


    public function delete($params) {
        $ids = $this->getParam($params, 'ids', array());
        if ($ids == 0) {
            return $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid ID.'));
        }
      
        if (!is_array($ids)) $ids = array($ids);
        
        foreach($ids as $id) {
            $model = PageWidget::model()->findByPk($id);
            if (is_object($model))
            {
                $allowDelete = FSM::_run('Cms.pageWidget.canDeleteWidget', array('page_widget_id'=>$model->id))->allowDelete;
                if ($allowDelete)
                {
                    try {
                        $model->delete();
                    } catch (CDbException $ex) {
                        $this->result->fail(ERROR_HANDLING_DB, $ex->getMessage());
                    }
                }
            }
            else
                $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget', 'Not found page_widget'));
        }
            
        return $this->result;
    }
    
    public function canDeleteWidget($params)
    {
        $id = $this->getParam($params, 'page_widget_id', 0);
        
        $allowDelete = true;
        $model = PageWidget::model()->findByPk($id);
        if (is_object($model) && $model->widget_settings_id)
        {
            $allowDelete = false;
            $this->result->fail(ERROR_HANDLING_DB, Yii::t('Cms.PageWidget', 'You can not delete widget of template.'));
        }
        
        $this->result->processed('allowDelete', $allowDelete);
        return $this->result;
    }
    
    /**
    * render HTML of a widget instance
    * 
    * @param int id page_widget id
    */
    public function renderHtml($params)
    {
        $pageWidgetId = $this->getParam($params,'id',0);
        
        // Version 2:
        // Passing the following 2 parameters might not be appropriated any more.
        // The new mechanism require pagewidget object so we have the widget setting
//        $widgetLayoutId = $this->getParam($params,'widget_layout_id',0);
//        $widgetDataId = $this->getParam($params,'widget_data_id',0);
        
        $pageWidget = null;
        $htmlDoc = new FDom('1.0','utf-8');
            
        if ($pageWidgetId)
        {
            $pageWidget = PageWidget::model()->findByPk($pageWidgetId);
            if (!$pageWidget)
                return $this->result->fail(ERROR_INVALID_DATA,Yii::t('CmsModule.PageWidget','Cannot find the PageWidget object for the given ID.'));
                
            $html = $pageWidget->renderHtml();
            if (Yii::app()->session->get('editMode', false) == true) {
                $params = array();
                if (is_string($pageWidget->widget_settings))
                    $params = $this->evaluateExpression($pageWidget->widget_settings);
                if (is_array($params))
                {
                    if (isset($params['widget']))
                    {
                        if (isset($params['widget']['criteria']))
                            $params = array('page_widget_id'=>$pageWidgetId);
                        else
                            $params = $params['widget'];
                    }
                }
                else
                    $params = array();
                $editUrl = url($pageWidget->widget->path.'/update',$params);
                // in edit mode, add the wrapper div with widget info as id and class
                $html = '<div id="page_widget_'.$pageWidgetId.'" class="sb-widget" rel="'.urlencode($editUrl).'">'
                        .$pageWidget->renderHtml()
                        .'</div>';
            }
//            $tmpDoc = new FDom('1.0','utf-8');
            $htmlDoc->loadXMLwithEntities($html,'root');
            if ($htmlDoc->getError() != '') {
                $html = '<div id="page_widget_'.$pageWidgetId.'" class="sb-widget" rel="'.urlencode($pageWidget->widget->path).'">'
                        .$htmlDoc->getError()
                        .'</div>';
                $htmlDoc->loadXMLwithEntities($html,'root');
            }

//            $elmTag = $htmlDoc->importNode($tmpDoc->childNodes->item(1), true);
            
            
//            /** @var WidgetData */
//            $instance = $pageWidget->widgetData;
//            /** @var WidgetLayout */
//            $layout = $pageWidget->widgetLayout;
        }
//        elseif ($widgetLayoutId && $widgetDataId)
//        {
//            $instance = WidgetData::model()->findByPk($widgetDataId);
//            $layout = WidgetLayout::model()->findByPk($widgetLayoutId);
//        }

//echo $htmlDoc->saveHTML();
        
        $this->result->processed('html',$htmlDoc);
        return $this->result;

        
        /**
        * If instance not found then we try to output the pageWidget object's details to user if he is in the Edit mode
        */
        if (empty($instance)) {
            if (Yii::app()->session->get('editMode', false) == true) {
                Yii::trace("Page contains a widget that is not found.\n".print_r($pageWidget->attributes,true));
                $errorContent = "<p><strong>ERROR: Widget ID not found !</strong><br /><pre>".print_r($pageWidget->attributes,true)."</pre></p>";
                $htmlDoc->loadXMLwithEntities($errorContent);
            } else {
                $htmlDoc->loadXMLwithEntities('<div></div>');
            }
            $this->result->processed('html',$htmlDoc);
            return $this->result;                
        }
        
        $xmlInstance = $this->buildXmlWidgetInstance($instance);
        $instanceQuery = new DOMXPath($xmlInstance);

        // create repetitive sections for multi-value sets
        $htmlDoc->loadXMLwithEntities($layout->layout);
        $htmlQuery = new DOMXPath($htmlDoc);
        $multiSections = $htmlQuery->query('//@multiple');
        
        for($i=0; $i<$multiSections->length; $i++)
        {
            // get multiple set ID
            $attr = $multiSections->item($i);
            $sectionName = $attr->value;
            $section = $attr->parentNode;
            if ($section->hasAttribute('multiple'))
                $section->removeAttribute('multiple');
            // find the set in the instance
            $set = $instanceQuery->query("multiple[@id='{$sectionName}']")->item(0);            
            // create place holders
            $count = $set->getAttribute('count');
            for($si = 1; $si <= $count; $si++)
            {
                if ($si < $count)
                {
                    $newSection = $section->cloneNode(true);
                    $section->parentNode->insertBefore($newSection, $section);
                }
                else
                {
                    $newSection = $section;
                }
                // identify Id of multi-value elements
                $elms = $htmlQuery->query('elm',$newSection);
                for($c=0; $c<$elms->length; $c++)
                {
                    $elm = $elms->item($c);
                    $elm->setAttribute('name',$elm->getAttribute('name')."[{$si}]");
                    if ($elm->hasAttribute('wrapper'))
                        $elm->setAttribute('wrapper',$elm->getAttribute('wrapper')."[{$si}]");
                }
            }
        }
        
        // parse data into layout
        $elms = $htmlDoc->getElementsByTagName('elm');
        for($i = $elms->length-1; $i >=0; $i--){
//            echo $htmlDoc->saveHTML(),"\n";
            $elm = $elms->item($i);
            $elmName = $elm->getAttribute('name');
            $wrapper = $elm->getAttribute('wrapper');
            
            $elmMeta = $instanceQuery->query("//*[@id='{$elmName}']")->item(0);
            if ($elmMeta instanceof DOMNode)
                $elmTag = $htmlDoc->importNode($elmMeta,true);
            else
            {
                continue;
                /*echo CHtml::encode($layout->layout).$layout->id.',page_widget_id: '.$pageWidget->id;
                echo $elmName;exit;*/
            }
            
            if ($elmTag->nodeName == 'text')
                $elmTag = new DOMText($elmTag->nodeValue);
            elseif ($elmTag->nodeName == 'html')
            {
                $tmpDoc = new DOMDocument('1.0','utf-8');
                $tmpDoc->loadXML("<div><![CDATA[{$elmTag->nodeValue}]]></div>");
                $elmTag = $htmlDoc->importNode($tmpDoc->childNodes->item(0), true);
            }
            elseif ($elmTag->nodeName == 'a')
            {
                $elmTag->appendChild(new DOMText($elmTag->getAttribute('innerText')));
                $elmTag->removeAttribute('innerText');
                $onclick = $elmTag->getAttribute('onclick');
                if (empty($onclick))
                    $elmTag->removeAttribute('onclick');
            }
            elseif ($elmTag->nodeName == 'img')
            {
                $src = $elmTag->getAttribute('src');
                if (stripos($src, 'http://') === false)
                    $src = Yii::app()->request->getBaseUrl(true) . '/' . $src;
                //prepend image source with site's absolute url
                $elmTag->setAttribute('src', $src);
            }
            elseif ($elmTag->nodeName === 'file')
            {
                $title = $name = $elmTag->getAttribute('name');
                $description = $elmTag->getAttribute('description');
                if (empty($description) === false)
                    $title = $description;
                
                $urlManager = Yii::createComponent(array('class'=>'CUrlManager'));
                $url = Yii::app()->getRequest()->getHostInfo('').$urlManager->createUrl('/Core/service/command', array(
                    'SID'=>'Cms.File.cmdDownloadWidgetFile', 
                    'wid'=>$instance->id, 
                    'fid'=>$elmTag->getAttribute('id'), 
                ), '&');
                $node = $htmlDoc->createElement("a");
                $elmTag = $htmlDoc->appendChild($node);
                $elmTag->setAttribute("title", $title);
                $elmTag->setAttribute("href", $url);
                $elmTag->appendChild(new DOMText($name));
            }
            elseif ($elmTag->nodeName === 'video')
            {
                $config = array();
                foreach ($elmTag->attributes as $attr)
                {
                    $config[$attr->nodeName] = $attr->nodeValue;
                }
                if ((isset($config['file']) && empty($config['file']) === false) || (isset($config['playlist']) && empty($config['playlist']) === false))
                {
                    $config['options'] = array();
                    if (isset($config['name']))
                    {
                        $config['id'] = $config['name'];
                        unset($config['name']);
                    }
                    if (isset($config['autostart']))
                    {
                        $config['options']['autostart'] = $config['autostart'] === 'true' ? true : false;
                        unset($config['autostart']);
                    }
                    $content = Yii::app()->controller->widget('Cms.extensions.MediaPlayer.MediaPlayerWidget', $config, true);
                }
                else
                {
                    $content = '';
                }
                $tmpDoc = new DOMDocument('1.0','utf-8');
                $tmpDoc->loadXML("<div>{$content}</div>");
                $elmTag = $htmlDoc->importNode($tmpDoc->childNodes->item(0), true);
            }
            elseif ($elmTag->nodeName === 'mtag')
            {
                // register meta tags
                $name = null;
                if ($elmTag->hasAttribute('name'))
                    $name = $elmTag->getAttribute('name');
                $content = '';
                if ($elmTag->hasAttribute('content'))
                    $content = $elmTag->getAttribute('content');
                $httpEquiv = null;
                if ($elmTag->hasAttribute('equiv'))
                    $httpEquiv = $elmTag->getAttribute('equiv');
                if ($name == 'title')
                    Yii::app()->controller->pageTitle = $content;
                else
                {
                    $options = array();
                    foreach ($elmTag->attributes as $attr)
                    {
                        if (!in_array($attr->nodeName, array('name', 'content', 'equiv', 'id')))
                        {
                            if (Yii::app()->user->hasFlash('fb-meta') == false && $attr->nodeName == 'property' && in_array($attr->nodeValue, array('og:title', 'og:type', 'og:url', 'og:image', 'og:site_name')))
                            {
                                Yii::app()->user->setFlash('fb-meta', 1);
                            }
                            $options[$attr->nodeName] = $attr->nodeValue;
                        }
                    }
                    Yii::app()->clientScript->registerMetaTag($content, $name, $httpEquiv, $options);
                }
                Yii::trace('registerMetaTag: '.$content.', '.$name.', '.$httpEquiv);
                $elmTag = $htmlDoc->importNode(new DOMText(''), true);
            }
            elseif ($elmTag->nodeName === 'com')
            {
                Yii::import($elmName);
                $reflectionClass = new ReflectionClass(substr(strrchr($elmName, '.'), 1));
                $config = array();
                foreach ($elmTag->attributes as $attr)
                {
                    $property = $attr->nodeName;
                    if ($reflectionClass->hasProperty($property) || $reflectionClass->hasMethod('set'.ucfirst($property)))
                    {
                        $config[$property] = $attr->nodeValue;
                    }
                    else
                    {
                        Yii::log("Unable to set value for property '{$property}'. It is possible the property is defined in the widget definition but not the widget class.");
                    }
                }
                Yii::trace('node: '.$elmTag->nodeName, 'attributes');
                Yii::trace(CVarDumper::dumpAsString($config), 'attributes');
                unset($config['id']);
                $widget = Yii::app()->controller->createWidget($elmName, $config);
                /*foreach ($elmTag->attributes as $attr)
                {
                    $property = $attr->nodeName;
                    try {
                    $widget->$property = $attr->nodeValue;
                    } catch (Exception $ex) {
                        Yii::log("Unable to set value for property '{$property}'. It is possible the property is defined in the widget definition but not the widget class.");
                    }
                }*/
                $html = $widget->run();

                $tmpDoc = new FDom('1.0','utf-8');
                $tmpDoc->loadXMLwithEntities($html,'com');

                $elmTag = $htmlDoc->importNode($tmpDoc->childNodes->item(1), true);
            }
            Yii::trace('nodeName: '.$elmTag->nodeName);
            
            // assing elm id
            switch($elm->getAttribute('id'))
            {
                case '':
                    if ($elmTag instanceof DOMText) break;
                    $elmTag->removeAttribute('id');
                break;
                case 'id':
                    //keep the id defined in the definition, nothing todo here
                break;
                case 'wid':
                    if ($elmTag instanceof DOMText) break;
                    if (is_object($pageWidget))
                        $elmTag->setAttribute('id',$elmTag->getAttribute('id').'-'.$pageWidget->id);//TODO
                break;
            }

            if ($wrapper)
            {
                $htmlDocXPath = new DOMXPath($htmlDoc);
                $wrapperMeta = $htmlDocXPath->query("//*[@id='{$wrapper}']")->item(0);
                if (!is_object($wrapperMeta))
                    $wrapperMeta = $instanceQuery->query("//*[@id='{$wrapper}']")->item(0);
                $wrapTag = $htmlDoc->importNode($wrapperMeta, true);
                $wrapTag->appendChild($elmTag);
                // assign wrapper id
                if (!($elmTag instanceof DOMText) && $elmTag->getAttribute('id') != '')
                    $wrapTag->setAttribute('id', $wrapTag->getAttribute('id').'-'.$elmTag->getAttribute('id'));
                
                $elmTag = $wrapTag;                
            }
            
            $parent = $elm->parentNode;
            $parent->replaceChild($elmTag, $elm);
        }
        
        // parse JS into layout
        $root = $htmlDoc->childNodes->item(0);
        $scripts = $xmlInstance->getElementsByTagName('js');
        for ($i=0; $i < $scripts->length; $i++)
        {
            $script = $scripts->item($i);
            /*if ($script->hasAttribute('script'))
            {
                $nodeValue = $script->getAttribute('script');var_dump($script->getAttribute('script'));exit;
                if (substr($nodeValue, -3) !== '.js')
                {
                    $script->nodeValue = htmlentities($nodeValue);
                    $script->removeAttribute('script');
                }
            }*/
            
            if ($script->nodeValue)
            {
                if (is_object($pageWidget))
                    $script->nodeValue = str_replace('{wid}',$pageWidget->id,$script->nodeValue);
            }
            $node = $htmlDoc->importNode($script, true);
            $htmlDoc->documentElement->appendChild($node);
        }
        // fix {wid} in JS code
        $scripts = $htmlDoc->getElementsByTagName('js');
        for ($i=0; $i < $scripts->length; $i++)
        {
            $script = $scripts->item($i);
            if (strpos($script->getAttribute('id'),'{wid}') !== false)
                if (is_object($pageWidget))
                    $script->setAttribute('id', str_replace('{wid}', $pageWidget->id,$script->getAttribute('id')));
            $nodeValue = $script->nodeValue;
            if (empty($nodeValue) === false)
                if (is_object($pageWidget))
                    $nodeValue = str_replace('{wid}',$pageWidget->id,$nodeValue);
            $script->nodeValue = htmlentities($nodeValue);
        }

        // uncomment to see the final widget HTML code after parsing
        // echo $htmlDoc->saveHTML();
        
        if (is_object($pageWidget))
        {
            //info
            //widget_id, widget_data_id, widget_layout_id
            $dataClass = ' widget_'.$instance->widget_id.' widget_instance_'.$instance->id.' widget_layout_'.$layout->id;
            if ($pageWidget->status == false)//TODO
                $dataClass .= ' hidden';
            if (Yii::app()->request->getParam('page_widget_id', 0) == $pageWidget->id)//TODO
                $dataClass .= ' sb-highlight';
            //find class="sb-widget"
            $items = $htmlQuery->query('//*[@class]');
            $node = null;
            if ($items->length)
            {
                foreach ($items as $index => $item)
                {
                    $class = $item->getAttribute('class');
                    if (strpos($class, 'sb-widget') !== false)
                    {
                        $node = $item;
                        break;
                    }
                }
            }
            if (is_object($node))
            {
                $node->setAttribute('id', 'page_widget_'.$pageWidget->id);//TODO
                $class = $node->getAttribute('class');
                if (!empty($class))
                    $class .= ' ';
                $class .= $dataClass;
                $node->setAttribute('class', $class);
            }
            else
            {
                $length = $htmlDoc->documentElement->childNodes->length;
                Yii::trace($htmlDoc->documentElement->nodeName.': '.$length);
                if ($length == 1)
                {
                    $node = $htmlDoc->documentElement->childNodes->item(0);
                    $node->setAttribute('id', 'page_widget_'.$pageWidget->id);//TODO
                    $class = $node->getAttribute('class');
                    if (!empty($class))
                        $class .= ' ';
                    $class .= 'sb-widget'.$dataClass;
                    $node->setAttribute('class', $class);
                    Yii::trace('update id, class for: '.$node->nodeName);
                }
                else if ($length > 1)//add wrapper
                {
                    //check have element list(li, dt, dd)
                    $hasListElement = false;
                    foreach ($htmlDoc->documentElement->childNodes as $index => $node)
                    {
                        if (in_array(strtolower($node->nodeName), array('li', 'dt', 'dd')))
                        {
                            $hasListElement = true;
                            break;
                        }
                    }
                    if ($hasListElement)
                    {
                        $node = $htmlDoc->documentElement->childNodes->item($index);
                        $node->setAttribute('id', 'page_widget_'.$pageWidget->id);//TODO
                        $class = $node->getAttribute('class');
                        if (!empty($class))
                            $class .= ' ';
                        $class .= 'sb-widget'.$dataClass;
                        $node->setAttribute('class', $class);
                        Yii::trace('update id, class for: '.$node->nodeName);
                    }
                    else
                    {
                        Yii::trace('wrapper div '.$length);
                        $wrapper = $htmlDoc->createElement('div');
                        $wrapper->setAttribute('id', 'page_widget_'.$pageWidget->id);//TODO
                        $wrapper->setAttribute('class', 'sb-widget'.$dataClass);
                        for ($i=0; $i<$length; $i++)
                        {
                            $child = $htmlDoc->documentElement->childNodes->item($i);
                            if (is_object($child) && !in_array($child->nodeName, array('js')))
                                $wrapper->appendChild($child);
                        }
                        $htmlDoc->documentElement->appendChild($wrapper);
                    }
                }
            }
        }
        $this->result->processed('html',$htmlDoc);
        return $this->result;
    }
    
    /**
    * Create DomDocument from a WidgetData object by combination its data with its definition
    * 
    * @param WidgetData $instance
    * @return DOMDocument
    */
    protected function buildXmlWidgetInstance($instance)
    {
        $definition = $instance->widget;
        
        $doc = new DOMDocument('1.0','utf-8');
        $doc->loadXML("<root>{$definition->dtd}</root>");
        $root = $doc->childNodes->item(0); //down one level as root is just a dummy node
    
        eval("\$data = {$instance->data};");
        for($i=0; $i < $root->childNodes->length; $i++)
        {
            $node = $root->childNodes->item($i);
            if (count($node->attributes))
            {
                if ($node->nodeName == 'multiple')
                {
                    $length = $node->childNodes->length;
                    for($mi=0; $mi < $length; $mi++)
                    {
                        $mnode = $node->childNodes->item($mi);
                        if (count($mnode->attributes))
                        {
                            if (in_array($mnode->nodeName,array('text','html')))
                                $count = $this->parseTextNode($mnode, $data, 1);
                            elseif (in_array($node->nodeName,array('js')))
                                $this->parseJsNode($node, $data, 1);
                            else
                                $count = $this->parseHtmlNode($mnode, $data, 1);
                        }
                    }
                    $node->setAttributeNode(new DOMAttr('count',$count));
                }
                elseif (in_array($node->nodeName,array('text','html')))
                    $this->parseTextNode($node, $data);
                elseif (in_array($node->nodeName,array('js')))
                    $this->parseJsNode($node, $data);
                else
                    $this->parseHtmlNode($node, $data);
            }
        }
        return $doc;
    }
    
    /**
    * Parse a text node in the Widget's DTD
    * 
    * @param DOMNode $node
    * @param array $data
    * @param bool $multiple
    */
    protected function parseTextNode(&$node, &$data, $multiple = 0)
    {
        if ($multiple) 
        {
            $cnode = $node->cloneNode();
            $parent = $node->parentNode;
        }

        $fieldName = $node->getAttribute('id');
        while(true)
        {
            $nodeData = isset($data[$fieldName]) ? $data[$fieldName] : '';
            if (!$multiple)
                $text = new DOMText($nodeData);
            else
            {
                $text = new DOMText($nodeData[$multiple-1]);
                $node->setAttribute('id',$fieldName."[{$multiple}]");
            }
            $node->appendChild($text);

            if (($multiple == 0) || ($multiple > 0 && $multiple == count($nodeData))) break;
            $node = $cnode->cloneNode();
            $parent->appendChild($node);
            $multiple++;
        }        
        return $multiple;
    }
    
    /**
    * Parse a JS node in the Widget's DTD
    * 
    * @param DOMNode $node
    * @param array $data
    * @param bool $multiple
    */
    protected function parseJsNode(&$node, &$data, $multiple = 0)
    {
        // static JS code, dont parse !
        if (strpos($node->getAttribute('script'),'input:') !== 0) return;
        
        if ($multiple) 
        {
            $cnode = $node->cloneNode();
            $parent = $node->parentNode;
        }

        $fieldName = $node->getAttribute('id');
        while(true)
        {
            $nodeData = isset($data[$fieldName]) ? $data[$fieldName] : '';
            $inline = true;
            if ($node->getAttribute('script') == 'input:file')
                $inline = false;
                
            if (!$multiple)
                $text = $nodeData;
            else
            {
                $text = $nodeData[$multiple-1];
                $node->setAttribute('id',$fieldName."[{$multiple}]");
            }
            
            $node->setAttribute('script','');
            if ($inline)
                $node->appendChild(new DOMText($text));
            else
                $node->setAttribute('script',$text);

            if (($multiple == 0) || ($multiple > 0 && $multiple == count($nodeData))) break;
            $node = $cnode->cloneNode();
            $parent->appendChild($node);
            $multiple++;
        }        
        return $multiple;
    }
    
    /**
    * Parse a HTML node in the Widget's DTD
    * 
    * @param DOMNode $node
    * @param array $data
    * @param bool $multiple
    */
    protected function parseHtmlNode(&$node, &$data, $multiple = 0)
    {
        if ($multiple) 
        {
            $cnode = $node->cloneNode();
            $parent = $node->parentNode;
        }
        $fieldId = $node->getAttribute('id');
        while(true)
        {
            foreach($node->attributes as $attr)
            {
                if (strpos($attr->value,'input:') === 0)
                {
                    $fieldNames[] = $fieldName = "{$fieldId}_{$attr->name}";
                    $value = isset($data[$fieldName]) ? $data[$fieldName] : '';
                    if (!$multiple)
                        $attr->value = $value;
                    else
                        $attr->value = isset($value[$multiple-1]) ? $value[$multiple-1] : '';
                }
            }
            if ($multiple) 
            {
                $node->setAttribute('id',$fieldId."[{$multiple}]");
            }
            
            if (($multiple == 0) || ($multiple > 0 && $multiple == count($value))) break;
            $node = $cnode->cloneNode();
            $parent->appendChild($node);
            $multiple++;
        }
        return $multiple;
    }
    
    public function updateContainer($params)
    {
        $container = $this->getParam($params, 'container', '');
        $pageWidgetId = $this->getParam($params, 'page_widget', 0);
        
        if (empty($container))
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid Container.'));
        if (!$pageWidgetId)
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid pageWidgetId.'));
            
        $rowUpdated = PageWidget::model()->updateByPk($pageWidgetId, array('container'=>$container));
        
        if (!$rowUpdated)
            $this->result->fail(ERROR_HANDLING_DB, Yii::t('Cms.PageWidget','Error while saving submitted data into database.'));
            
        $this->result->processed('info', array(
            'container'=>$container,
            'pageWidgetId'=>$pageWidgetId,
            'rowUpdated'=>$rowUpdated,
        ));
        
        return $this->result;
    }
    
    public function updateOrder($params)
    {
        $container = $this->getParam($params, 'container', '');
        $items = $this->getParam($params, 'page_widget', array());
        
        if (empty($container))
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid Container.'));
            
        if (count($items))
        {
            $rowsUpdated = array();
            foreach ($items as $index => $id)
            {
                $updated = PageWidget::model()->updateByPk($id, array('ordering'=>$index+1));
                $rowsUpdated[$id] = array(
                    'order'=>$index+1,
                    'updated'=>(boolean) $updated,
                );
            }
            $this->result->processed('info', array(
                'container'=>$container,
                'rowsUpdated'=>$rowsUpdated
            ));
        }
        return $this->result;
    }
    
    /**
    * all page_widget_id must same container
    * update order widget by page_widget_id 
    * 
    * @param array $params
    */
    public function order($params)
    {
        $ids = $this->getParam($params, 'id', array());
        if (count($ids))
        {
            foreach ($ids as $index => $id)
            {
                PageWidget::model()->updateByPk($id, array('ordering'=>$index+1));
            }
        }
        return $this->result;
    }
    
    public function removeWidget($params)
    {
        $pageWidgetId = $this->getParam($params, 'id', 0);
        $pageId = $this->getParam($params, 'page_id', 0);
        $widgetDataId = $this->getParam($params, 'widget_data_id', 0);
        $widgetLayoutId = $this->getParam($params, 'widget_layout_id', 0);
        $container = $this->getParam($params, 'container', '');
        
        if ($pageWidgetId)
        {
            $result = PageWidget::model()->deleteByPk($pageWidgetId);
        }
        else
        {
            $attributes = array(
                'page_id'=>$pageId,
                'widget_data_id'=>$widgetDataId,
                'widget_layout_id'=>$widgetLayoutId,
                'container'=>$container,
            );
            $result = PageWidget::model()->deleteAllByAttributes($attributes);
        }
        
        $this->result->processed('info', array(
            'deleted'=>$result,
            'data'=>$attributes,
        ));
        
        return $this->result;
    }
    
    public function renderHtmlByAttributes($params)
    {
        $pageId = $this->getParam($params, 'page_id', 0);
        $widgetDataId = $this->getParam($params, 'widget_data_id', 0);
        $widgetLayoutId = $this->getParam($params, 'widget_layout_id', 0);
        $container = $this->getParam($params, 'container', '');
        
        $attributes = array(
            'page_id'=>$pageId,
            'widget_data_id'=>$widgetDataId,
            'widget_layout_id'=>$widgetLayoutId,
            'container'=>$container,
        );
        $model = PageWidget::model()->findByAttributes($attributes);
        if (is_object($model))
        {
            $this->renderHtml(array('id'=>$model->id));
        }
        return $this->result;
    }
    
    public function dynamicAddWidget($params)
    {
        $pageWidgetId = $this->getParam($params, 'page_widget_id', 0);
        if ($pageWidgetId)
            $dom = $this->renderHtml(array('id'=>$pageWidgetId))->html;
        else
            $dom = $this->renderHtmlByAttributes($params)->html;
            
        $script = "parent.window.location = parent.window.location;\n"
            ."parent.\$.fancybox.close();";
        if (is_object($dom))
        {
            //detect js tag
            $scripts = $dom->getElementsByTagName('js');
            if ($scripts->length <= 0)
            {
                $html = $dom->saveHTML();
                //remove newline
                $html = trim($html);
                $html = str_replace("\n", "", $html);
                $html = str_replace("\r", "", $html);
                if (preg_match('/<root>(.+)?<\/root>/im', $html, $matches))
                {
                    $html = addslashes(trim($matches[1]));
                }
                //TODO hotfix widget must be reload page(MediaPlayer, ...)
                $reloadPage = stripos($html, 'Loading the player ...') !== false;
                if ($reloadPage == false)
                    $script = "parent.\$.addWidget('{$html}');parent.\$('#sb-nav-close-form').trigger('click');";
            }
        }
        Yii::app()->clientScript->registerScript(__CLASS__.'#'.__FUNCTION__, $script, CClientScript::POS_READY);
        return $this->result;
    }
    
    public function savePosition($params)
    {
        $pageId = $this->getParam($params, 'page_id', 0);
        $revisionId = $this->getParam($params, 'revision', 0);
        $containers = $this->getParam($params, 'containers', null, 'xss, newline, tag');
        $isNewRevision = (boolean) $this->getParam($params, 'new_revision', false);
        
        if ($isNewRevision)
        {
            $setActive = (boolean) $this->getParam($params, 'set_active', false);
            $log = $this->getParam($params, 'log', '');
            if (empty($log))
            {
                return $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','You must enter a log message for the new revision.'));
            }
            $this->create($pageId, $revisionId, $containers, $log, $setActive);
        }
        else
        {
            $this->update($pageId, $revisionId, $containers);
        }
    }
    
    /**
    * update position of widget in each containers
    * remove unused widgets
    * 
    * @param int $pageId
    * @param int $revisionId
    * @param array $containers
    *   array(
    *       'container_id'=>array(
    *           page_widget_id,
    *       )
    *   )
    * @param boolean $revisionId
    * @param string $log
    */
    protected function create($pageId, $revisionId, $containers, $log, $setActive=false)
    {
        //find widgets in containers cms-header, cms-footer
        $criteria = new CDbCriteria();
        $criteria->compare('page_id', $pageId);
        $criteria->compare('revision_id', $revisionId);
        if (is_array($containers))
        {
            $criteria->order = 'ordering';
            $criteria->addInCondition('container', array('cms-header', 'cms-footer'));
        }
        else
        {
            $criteria->order = 'container, ordering';
        }
        $models = PageWidget::model()->findAll($criteria);
        if (count($models))
        {
            foreach ($models as $model)
            {
                if (isset($containers[$model->container]))
                {
                    $containers[$model->container][] = (int) $model->id;
                }
                else
                {
                    $containers[$model->container] = array((int) $model->id);
                }
            }
        }
        //find last revision 
        $nextRevisionId = (int) FSM::_run('Cms.PageRevision.getNextRevision',array('page_id'=>$pageId))->revision;
        if (count($containers))
        {
            //copy page_widget rows
            foreach ($containers as $containerId => $pageWidgetIds)
            {
                if (is_array($pageWidgetIds) && count($pageWidgetIds))
                {
                    foreach ($pageWidgetIds as $index => $pageWidgetId)
                    {
                        $model = PageWidget::model()->findByPk($pageWidgetId);
                        if (is_object($model))
                        {
                            $model->setIsNewRecord(true);
                            $model->id = null;
                            $model->container = $containerId;
                            $model->ordering = $index;
                            $model->revision_id = $nextRevisionId;
                            if (! $model->save())
                            {
                                Yii::log(CVarDumper::dumpAsString($model->getErrors()), CLogger::LEVEL_ERROR);
                            }
                        }
                    }
                }
            }
            //copy page revision
            $currentPageRevision = PageRevision::model()->findByAttributes(array('page_id'=>$pageId, 'id'=>$revisionId));
            if (is_object($currentPageRevision))
            {
                FSM::_run('Cms.PageRevision.save',array(
                    'PageRevision'=>array(
                        'id'=>$nextRevisionId,
                        'page_id'=>$pageId,
                        'template_content_id'=>$currentPageRevision->template_content_id,
                        'js_files'=>$currentPageRevision->js_files,
                        'css_files'=>$currentPageRevision->css_files,
                        'created_by'=>Yii::app()->user->id,
                        'updated_by'=>Yii::app()->user->id,
                        'creation_datetime'=>date('Y-m-d H:i:s'),
                        'last_update'=>date('Y-m-d H:i:s'),
                        'log'=>$log,
                    )
                ));
                //set this revision is active in workflow
                if ($setActive)
                {
                    FSM::_run('Cms.Page.revert', array('page_id'=>$pageId,'revision'=>$nextRevisionId));
                }
            }
        }
    }
    
    /**
    * update position of widget in each containers
    * remove unused widgets
    * 
    * @param int $pageId
    * @param int $revisionId
    * @param array $containers
    *   array(
    *       'container_id'=>array(
    *           page_widget_id,
    *       )
    *   )
    */
    protected function update($pageId, $revisionId, $containers)
    {
        $pageWidgetIds = array();
        //update position
        if (is_array($containers) && count($containers))
        {
            foreach ($containers as $containerId => $data)
            {
                if (is_array($data) && count($data))
                {
                    foreach ($data as $order => $pageWidgetId)
                    {
                        PageWidget::model()->updateByPk($pageWidgetId, array(
                            'container'=>$containerId,
                            'ordering'=>$order,
                        ));
                        FSM::_run('Cms.PageRevision.updateBy',array('id' => $revisionId, 'page_id'=>$pageId));
                        array_push($pageWidgetIds, (int) $pageWidgetId);
                    }
                }
            }
        }
        //delete unused widgets
        $criteria = new CDbCriteria();
        $criteria->compare('page_id', $pageId);
        $criteria->compare('revision_id', $revisionId);
        $criteria->addCondition('widget_settings_id IS NULL OR widget_settings_id = 0');
        $criteria->addNotInCondition('container', array('cms-header','cms-footer'));
        $criteria->addNotInCondition('id', $pageWidgetIds);
        PageWidget::model()->deleteAll($criteria);
    }
    
    public function getItemsHiddenContainer($params)
    {
        $pageId = $this->getParam($params, 'page_id', 0);
        $revision = $this->getParam($params, 'revision_id', 0);
        $type = $this->getParam($params, 'type', '');
        
        $widgets = PageWidget::model()->findAllByAttributes(array(
            'page_id'=>$pageId,
            'revision_id'=>$revision,
            'container'=>$type
        ), array('order'=>'ordering'));
        $items = array();
        if (count($widgets))
        {
            foreach ($widgets as $widget)
            {
                $params = array();
                if (is_string($widget->widget_settings))
                    $params = $this->evaluateExpression($widget->widget_settings);
                if (is_array($params))
                {
                    if (isset($params['widget']))
                    {
                        if (!isset($params['widget']['criteria']))
                            $params = $params['widget'];
                    }
                }
                else
                    $params = array();
                $params['page_widget_id'] = $widget->id;
                array_unshift($params, '/'.trim($widget->widget->path,'/').'/update');
                
                $name = $widget->widget->name.'('.$widget->id.')';
                //show template info for widgets of template
                if ($widget->widget_settings_id)
                {
                    $name = '<span class="disable-delete">'.$name;
                    $pwModel = PageWidget::model()->findByPk($widget->widget_settings_id);
                    if (is_object($pwModel))
                    {
                        $tplModel = TemplateContent::model()->with(array('template'))->findByPk($pwModel->template_content_id);
                        if (is_object($tplModel))
                            $name .= '('.$tplModel->template->name.' #r'.$tplModel->revision_id.')';
                    }
                    $name .= '</span>';
                }
                $items['id-'.$widget->id] = CHtml::link($name,$params, array('class'=>'widget-title'));
//                $items['id-'.$widget->id] = $widget->widget->name.'('.$widget->id.')';
            }
        }
        
        $this->result->processed('items', $items);
        return $this->result;
    }
    
    /**
    * copy page_widget from site to site
    * 
    * @param array $params
    * <ul>
    *   <li>source_page_id: int, required, copy from source page_id</li>
    *   <li>target_page_id: int, required, copy to target page_id</li>
    *   <li>revisions_transfer: array(source_revision_id=>target_revision_id), required, revisions map</li>
    * </ul>
    * 
    * @return FServiceModel
    */
    public function copy($params)
    {
        $sourcePageId = $this->getParam($params, 'source_page_id', 0);
        $targetPageId = $this->getParam($params, 'target_page_id', 0);
        $revisionsTransfer = $this->getParam($params, 'revisions_transfer', array());
        $linkData = $this->getParam($params, 'link_data', false);
        
        $models = array();
        if ($sourcePageId && $targetPageId)
        {
            $criteria = new CDbCriteria();
            $criteria->compare('page_id', $sourcePageId);
            $criteria->order = 'revision_id ASC';
            $sourcePageWidgets = PageWidget::model()->findAll($criteria);
            if (count($sourcePageWidgets))
            {
                $data = array();
                foreach ($sourcePageWidgets as $sourcePageWidget)
                {
                    //get correct target revision_id
                    if (isset($revisionsTransfer[$sourcePageWidget->revision_id]))
                        $targetRevisionId = $revisionsTransfer[$sourcePageWidget->revision_id];
                    else
                        continue;
                        
                        
                    $sourcePageWidget->widget_settings = $this->evaluateExpression($sourcePageWidget->widget_settings);
                    if (!is_array($sourcePageWidget->widget_settings))
                        $sourcePageWidget->widget_settings = array();
                    
                    //don't duplicate widget_data
                    $widgetDataId = 0;
                    if ($linkData)
                    {
                        if (isset($sourcePageWidget->widget_settings['widget'], $sourcePageWidget->widget_settings['widget']['id']))
                            $widgetDataId = $sourcePageWidget->widget_settings['widget']['id'];
                    }
                    elseif (isset($sourcePageWidget->widget_settings['widget'], $sourcePageWidget->widget_settings['widget']['id'], $data[$sourcePageWidget->widget_settings['widget']['id']]))
                    {
                        $widgetDataId = $data[$sourcePageWidget->widget_settings['widget']['id']];
                    }
                    else
                    {
                        if (isset($sourcePageWidget->widget_settings['widget'], $sourcePageWidget->widget_settings['widget']['id']))
                        {
                            //copy data
                            if (is_object($sourcePageWidget->contentType))
                            {
                                /** @var CActiveRecord */
                                $widgetData = Yii::createComponent(array('class'=>$sourcePageWidget->contentType->model));
                                $widgetData->refresh();
                                /** @var CActiveRecord */
                                $newWidgetData = $widgetData->findByPk($sourcePageWidget->widget_settings['widget']['id']);
                                if (is_object($newWidgetData))
                                {
                                    $newWidgetData->setIsNewRecord(true);
                                    $newWidgetData->id = null;
                                    if ($newWidgetData->save())
                                    {
                                        $widgetDataId = $data[$sourcePageWidget->widget_settings['widget']['id']] = $newWidgetData->id;
                                    }
                                    else
                                    {
                                        Yii::log(CVarDumper::dumpAsString($newWidgetData->getErrors()), CLogger::LEVEL_ERROR);
                                    }
                                }
                                else
                                {
                                    Yii::log('Not found widget data id: '.$sourcePageWidget->widget_settings['widget']['id'], CLogger::LEVEL_ERROR);
                                }
                            }
                            else
                            {
                                Yii::log('Not content type with id: '.$sourcePageWidget->content_type_id, CLogger::LEVEL_ERROR);
                            }
                        }
                        if ($widgetDataId)
                        {
                            //update widget_settings
                            $settings = $sourcePageWidget->widget_settings;
                            $settings['widget']['id'] = $widgetDataId;
                            $sourcePageWidget->widget_settings = $settings;
                        }
                    }
                    /**
                    * @var PageWidget
                    */
                    $sourcePageWidget->setIsNewRecord(true);
                    $sourcePageWidget->id = null;
                    $sourcePageWidget->page_id = $targetPageId;
                    $sourcePageWidget->revision_id = $targetRevisionId;
                    $sourcePageWidget->widget_settings = var_export($sourcePageWidget->widget_settings, true);
                    if ($sourcePageWidget->save())
                    {
                        array_push($models, $sourcePageWidget);
                    }
                    else
                    {
                        Yii::log(CVarDumper::dumpAsString($sourcePageWidget->getErrors()), CLogger::LEVEL_ERROR);
                    }
                }
            }
        }
        else
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('CmsModule.Page', 'Submitted data is missing or invalid.'));
            
        $this->result->processed('model', $models);
        
        return $this->result;
    }

    public function deleteAllByPageId($params) {
        $pageId = $this->getParam($params, 'page_id', 0);
        if (!$pageId)
            return $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid ID.'.CVarDumper::dumpAsString($pageId)));
      
        PageWidget::model()->deleteAllByAttributes(array('page_id'=>$pageId));

        return $this->result;
    }

    public function deleteByPageIdAndWidgetId($params) {
        $pageId = $this->getParam($params, 'page_id', 0);
        $widgetId = $this->getParam($params, 'widget_id', 0);
        if (!$pageId || !$widgetId)
            return $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid ID.'.CVarDumper::dumpAsString($pageId)));
      
        $criteria = new CDbCriteria();
        $criteria->join = 'INNER JOIN '.SITE_TABLE_PREFIX.'widget_data wd ON wd.id = t.widget_data_id';
        $criteria->compare('page_id', $pageId);
        $criteria->compare('wd.widget_id', $widgetId);
        $models = PageWidget::model()->findAll($criteria);
        foreach($models as $model)
            $model->delete();
        FSM::_run('Cms.widgetData.deleteByPage', array('page_id'=>$pageId, 'widget_id'=>$widgetId));

        return $this->result;
    }
    
    public function changeStatus($params) {
        $ids = $this->getParam($params, 'ids', array());
        $value = $this->getParam($params, 'value', false);
        
        if (count($ids) <= 0)
            return $this->result;
        
        $criteria = new CDbCriteria();
        $criteria->addInCondition('id', $ids);
        PageWidget::model()->updateAll(array('status'=>$value), $criteria);
        
        return $this->result;
    }
    
    /**
    * add required widget when create new page(revision_id=1)
    * 
    * @param array $params
    * @return FServiceModel
    */
    public function addRequiredWidget($params)
    {
        $pageId = $this->getParam($params, 'page_id', 0);
        $widgetId = $this->getParam($params, 'widget_id', 0);
        
        if (!$pageId)
            return $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.PageWidget','Invalid ID.'.CVarDumper::dumpAsString($pageId)));
        
        if ($widgetId)
        {
            $model = WidgetDefinition::model()->findByPk($widgetId, 'status = :status', array(':status'=>true));
            $models = array($model);
        }
        else
            $models = FSM::_run('Cms.widgetDefinition.getRequired')->models;
        if (count($models))
        {
            foreach ($models as $model)
            {
                if (empty($model->container)) continue;
                
                $page = Page::model()->findByPk($pageId);
                $pageName = '';
                if (is_object($page))
                    $pageName = $page->title;
                
                //create blank widget data
                $widgetDataId = 0;
                $widgetData = new WidgetData();
                $widgetData->widget_id = $model->id;
                $widgetData->name = 'Default - '.$model->name.' - '.$pageName;
                $data = array();
                $widgetData->data = var_export($data, true);
                $widgetData->status = true;
                if ($widgetData->save())
                    $widgetDataId = $widgetData->id;
                else
                    CLogger::log(CVarDumper::dumpAsString($widgetData->getErrors()), CLogger::LEVEL_ERROR);
                    
                //find default layout of widget
                $widgetLayoutId = 0;
                $criteria = new CDbCriteria();
                $criteria->compare('widget_id', $model->id);
                $criteria->compare('status', true);
                $widgetLayout = WidgetLayout::model()->find($criteria);
                if (is_object($widgetLayout))
                    $widgetLayoutId = $widgetLayout->id;
                //add widget to page
                $pageWidget = new PageWidget();
                $pageWidget->page_id = $pageId;
                $pageWidget->container = $model->container;
                $pageWidget->revision_id = 1;
                $pageWidget->status = true;
                $pageWidget->widget_data_id = $widgetDataId;
                $pageWidget->widget_layout_id = $widgetLayoutId;
                if (!$pageWidget->save())
                {
                    CLogger::log(CVarDumper::dumpAsString($pageWidget->getErrors()), CLogger::LEVEL_ERROR);
                }
                    
            }
        }
        return $this->result;
    }
    
    public function renderStaticWidget($params)
    {
        $siteId = $this->getParam($params, 'site_id', 0);
        
        $html = '';
        if ($siteId)
        {
            $staticWidgets = FSM::_run('Cms.widgetData.getStaticWidget', array('site_id'=>$siteId))->models;
            if (is_array($staticWidgets) && count($staticWidgets))
            {
                foreach ($staticWidgets as $staticWidget)
                {
                    if (is_object($staticWidget->widget) && is_array($staticWidget->widget->layouts))
                    {
                        $layoutId = $staticWidget->widget->layouts[0]->id;
//                        var_dump($layoutId);
                        $htmlDoc = FSM::_run('Cms.pageWidget.renderHtml', array(
                            'widget_layout_id'=>$layoutId,
                            'widget_data_id'=>$staticWidget->id,
                        ))->html;
                        if (is_object($htmlDoc))
                        {
                            $staticHtml = $htmlDoc->saveXML($htmlDoc->getElementsByTagName('root')->item(0));
                            $staticHtml = str_replace(array('<root>', '</root>'), '', $staticHtml);
                            $html .= $staticHtml;
                        }
                    }
                }
            }
        }
        else
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('CmsModule.Page', 'Invalid site_id.'));
        
        $this->result->processed('html', $html);
        return $this->result;
    }
    
    /**
    * remove and reorder widgets in containers of template/page
    * 
    * @param array $params
    *   template_content_id int     template content id
    *   page_id             int     page id
    *   revision            int     page revision id
    *   containers          array   array('container'=>array(order=>page_widget_id))
    * @return FServiceModel
    */
    public function updateContainers($params)
    {
        $templateContentId = $this->getParam($params, 'template_content_id', 0);
        $pageId = $this->getParam($params, 'page_id', 0);
        $revisionId = $this->getParam($params, 'revision', 0);
        $containers = $this->getParam($params, 'containers', array(), 'xss, newline, tag');
        
        $criteria = new CDbCriteria();
        if ($templateContentId)
        {
            //find widgets of template
            //find all children template
            $tplIds = FSM::_run('Cms.templateContent.getChildrenTemplate', array('id'=>$templateContentId))->children;
            array_push($tplIds, $templateContentId);
            $criteria->addInCondition('template_content_id', $tplIds);
        }
        else
        {
            //find widgets of page
            $criteria->compare('page_id', $pageId);
            $criteria->compare('revision_id', $revisionId);
        }
        $oldPageWidgetIds = PageWidget::model()->findAll($criteria);
        
        //reorder
        $pageWidgetIds = array();
        if (count($containers))
        {
            foreach ($containers as $containerId => $data)
            {
                if (is_array($data) && count($data))
                {
                    foreach ($data as $order => $pageWidgetId)
                    {
                        array_push($pageWidgetIds, $pageWidgetId);
                        $model = PageWidget::model()->findByPk($pageWidgetId);
                        if (!is_object($model)) continue;
                        
                        $containerChanged = $model->container != $containerId;
                        if ($model->ordering != $order || $containerChanged)
                        {
                            $model->saveAttributes(array(
                                'container'=>$containerId,
                                'ordering'=>$order,
                            ));
                        }
                        //notify update date page revision
                        if ($pageId && $revisionId)
                        {
                            FSM::_run('Cms.PageRevision.updateBy',array('id' => $revisionId, 'page_id'=>$pageId));
                        }
                        //case update widgets of template when container changed
                        elseif ($templateContentId && $containerChanged)
                        {
                            //update container, ordering=0 for pages linked with current template
                            $models = PageWidget::model()->findAllByAttributes(array('widget_settings_id'=>$pageWidgetId));
                            foreach ($models as $model)
                            {
                                $criteria = new CDbCriteria;
                                $criteria->order = 'ordering DESC';
                                $criteria->compare('page_id', $model->page_id);
                                $criteria->compare('revision_id', $model->revision_id);
                                $criteria->compare('container', $containerId);
                                $ordModel = PageWidget::model()->find($criteria);
                                $model->saveAttributes(array(
                                    'container'=>$containerId,
                                    'ordering'=>(is_object($ordModel) ? $ordModel->ordering+1 : 0),
                                ));
                            }
                        }
                    }
                }
            }
        }
        
        //delete old page_widget_id
        if (count($oldPageWidgetIds))
        {
            $oldPageWidgetIds = CHtml::listData($oldPageWidgetIds, 'id', 'id');
            $removeIds = array_diff($oldPageWidgetIds, $pageWidgetIds);
            if (count($removeIds))
            {
                //remove widgets of template from pages are using template
                $criteria = new CDbCriteria();
                $criteria->addInCondition('widget_settings_id', $removeIds);
                PageWidget::model()->deleteAll($criteria);
                $criteria = new CDbCriteria();
                $criteria->addInCondition('id', $removeIds);
                PageWidget::model()->deleteAll($criteria);
            }
        }
        
        return $this->result;
    }
    
    public function duplicateData($params)
    {
        $pageWidgetId = $this->getParam($params, 'page_widget_id', 0);
        
        /** @var PageWidget $pageWidget */
        $pageWidget = PageWidget::model()->findByPk($pageWidgetId);
        if (is_object($pageWidget))
        {
            //duplicate widget of template from page
            if ($pageWidget->widget_settings_id)
            {
                $this->result->fail(ERROR_INVALID_DATA, Yii::t('CmsModule.Page', 'Can not duplicate widget of template from page.'));
            }
            else
            {
                $pageWidget->widget_settings = $this->evaluateExpression($pageWidget->widget_settings);
                if (!is_array($pageWidget->widget_settings))
                    $pageWidget->widget_settings = array();
                
                if (isset($pageWidget->widget_settings['widget'], $pageWidget->widget_settings['widget']['id']))
                {
                    //copy data
                    if (is_object($pageWidget->contentType))
                    {
                        /** @var CActiveRecord */
                        $widgetData = Yii::createComponent(array('class'=>$pageWidget->contentType->model));
                        $widgetData->refresh();
                        /** @var CActiveRecord */
                        $newWidgetData = $widgetData->findByPk($pageWidget->widget_settings['widget']['id']);
                        if (is_object($newWidgetData))
                        {
                            $newWidgetData->setIsNewRecord(true);
                            $newWidgetData->id = null;
                            if ($newWidgetData->save())
                            {
                                //update widget_settings
                                $settings = $pageWidget->widget_settings;
                                $settings['widget']['id'] = $newWidgetData->id;
                                $pageWidget->widget_settings = var_export($settings, true);
                            }
                            else
                            {
                                Yii::log(CVarDumper::dumpAsString($newWidgetData->getErrors()), CLogger::LEVEL_ERROR);
                            }
                        }
                        else
                        {
                            Yii::log('Not found widget data id: '.$pageWidget->widget_settings['widget']['id'], CLogger::LEVEL_ERROR);
                        }
                    }
                    else
                    {
                        Yii::log('Not content type with id: '.$pageWidget->content_type_id, CLogger::LEVEL_ERROR);
                    }
                }
                                
                /**
                * @var PageWidget
                */
                $pageWidget->setIsNewRecord(true);
                $pageWidget->id = null;
                $pageWidget->ordering = $pageWidget->ordering+1;
                if ($pageWidget->save())
                {
                    //update ordering
                    $criteria = new CDbCriteria;
                    $criteria->compare('page_id', $pageWidget->page_id);
                    $criteria->compare('revision_id', $pageWidget->revision_id);
                    $criteria->compare('container', $pageWidget->container);
                    $criteria->compare('id', '<>'.$pageWidget->id);
                    $criteria->compare('ordering', '>='.$pageWidget->ordering);
                    PageWidget::model()->updateAll(array('ordering'=>new CDbExpression('ordering+1')), $criteria);
                    
                    //if widget of template, update all pages using this template
                    if ($pageWidget->template_content_id)
                    {
                        $tplIds = FSM::_run('Cms.templateContent.getChildrenTemplate', array('id'=>$pageWidget->template_content_id))->children;
                        $tplIds = CPropertyValue::ensureArray($tplIds);
                        array_push($tplIds, $pageWidget->template_content_id);
                        $criteria = new CDbCriteria;
                        $criteria->select = array('page_id','id');
                        $criteria->distinct = true;
                        $criteria->addInCondition('template_content_id', $tplIds);
                        $models = PageRevision::model()->findAll($criteria);
                        if (is_array($models) && count($models))
                        {
                            foreach ($models as $linkedPage)
                            {
                                FSM::_run('Cms.templateContent.linkWidgetsToPage', array(
                                    'page_id'=>$linkedPage->page_id,
                                    'revision_id'=>$linkedPage->id,
                                    'template_content_id'=>$pageWidget->template_content_id,
                                ));
                            }
                        }
                    }
                }
                else
                {
                    Yii::log(CVarDumper::dumpAsString($pageWidget->getErrors()), CLogger::LEVEL_ERROR);
                }
            }
        }
        else
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('CmsModule.Page', 'Invalid page_widget_id.'));
        
        return $this->result->processed('model', $pageWidget);
    }
    
    /**
     * find all pages used widget data id
     * 
     * @param type $params 
     *  widget_data_id int required, widget data id
     * @return array page_widget_id => page title + page revision
     */
    public function getPagesUsedWidgetData($params)
    {
        $widgetDataId = $this->getParam($params, 'widget_data_id', 0);
        
        $pageWidgets = PageWidget::model()->findAll('widget_data_id = :widget_data_id',array(':widget_data_id' => $widgetDataId));
        $affectedPages = array();
        foreach($pageWidgets as $pw)
        {
            $affectedPages[$pw->id] = "<strong>{$pw->page->title}</strong> (Revision #{$pw->revision_id})";
        }
        
        return $this->result->processed('pages', $affectedPages);
    }
    
    public function updateLayout($params)
    {
        $pageWidgetId = $this->getParam($params, 'page_widget_id', 0);
        $widgetLayoutId = $this->getParam($params, 'widget_layout_id', 0);
        $result = PageWidget::model()->updateByPk($pageWidgetId, array('widget_layout_id'=>$widgetLayoutId));
        return $this->result->processed('status', $result);
    }
}
